import 'package:date_utils/date_utils.dart';
import "package:flutter/material.dart";

import './event.dart';
import './events.dart';
import '../../../../widget/triangle.widget.dart';

enum ViewType {
    week,
    month,
    year
}

class CalendarView<T> extends StatefulWidget {
    final int firstDayOfWeek;
    final double headerHeight = 50;
    final bool isFixedDayCount;
    final ViewType viewType;
    final Size viewSize;
    final DateTime selectedDate;
    final EventList<T> eventDatesMap;
    //final Widget Function(Event event, bool isToday, bool isSelectedDay, bool isThisMonthDay ) eventWidget;
    final Widget Function(DateTime date, bool isToday, bool isSelectedDay, bool isThisMonthDay ) dateWidget;
    final void Function(DateTime now, bool isToday) onDayPressed;
    final void Function(DateTime) onCalendarChanged;
    
    CalendarView({
        this.firstDayOfWeek = 0,
        this.isFixedDayCount = true,
        this.viewType = ViewType.month,
        this.selectedDate,
        this.eventDatesMap,
        this.viewSize,
        //this.eventWidget,
        this.dateWidget,
        this.onDayPressed,
        this.onCalendarChanged,
    });
    
    @override
    _CalendarViewState createState() => _CalendarViewState();
}

class _CalendarViewState extends State<CalendarView> {
    GlobalKey _containerKey = GlobalKey();
    PageController _controller;
    List<DateTime> _dates = List(3);
    List<List<DateTime>> _weeks = List(3);
    DateTime _selectedDate = format(DateTime.now());
    int _startWeekday = 0;
    int _endWeekday = 0;
    int pageIndex = 1;
    Size _viewSize;
    Widget Function(Event event, bool isToday, bool isSelectedDay, bool isThisMonthDay ) _eventWidget;
    Widget Function(DateTime date, bool isToday, bool isSelectedDay, bool isThisMonthDay ) _dateWidget;
    void Function(DateTime date, bool isToday) _onDayPressed;
    void Function(DateTime date) _onCalendarChanged;
    EventList<Event> _eventDatesMap = EventList<Event>();
    
    static const WeekHeight = 20; // 周一 周二 这一行 的高度
    
    static const Saturday = Color(0xFF2F76F9); // 周六的文字颜色
    static const Sunday = Color(0xFFEE5454); // 周日的文字颜色
    
    @override
    void initState() {
        // WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
        super.initState();
        if (widget.selectedDate != null) {
            _selectedDate = widget.selectedDate;
        }
        if (widget.eventDatesMap != null) {
            _eventDatesMap = widget.eventDatesMap;
        }
        //if (widget.eventWidget != null) {
        //    _eventWidget = widget.eventWidget;
        //}
        if (widget.dateWidget != null) {
            _dateWidget = widget.dateWidget;
        }
        if (widget.onDayPressed != null) {
            _onDayPressed = widget.onDayPressed;
        }
        if (widget.onCalendarChanged != null) {
            _onCalendarChanged = widget.onCalendarChanged;
        }
        // print(_onDayPressed);
        setDates(-1);
        setWeeks(-1);
        _controller = PageController(
            initialPage: pageIndex,
            keepPage: true,
            viewportFraction: 1.0,
        );
    }
    
    @override
    dispose() {
        _controller.dispose();
        super.dispose();
    }
    
    @override
    Widget build(BuildContext context) {
        // print('viewType : ${widget.viewType}');
        // print('context  : $context');
        // print('context  : ${MediaQuery.of(context)}');
        return LayoutBuilder(
            builder: (context, constraints) {
                // print(constraints.maxHeight);
                // print(MediaQuery.of(context).size.height);
                if (widget.viewSize == null) {
                    _viewSize = Size(constraints.maxWidth, constraints.maxHeight);
                } else {
                    _viewSize = widget.viewSize;
                }
                return Container(
                    // padding: EdgeInsets.only(top: 20),
                    child: Column(
                        mainAxisSize: MainAxisSize.min,
                        children: <Widget>[
                            Container(
                                height: widget.headerHeight,
                                child: Row(
                                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                                    // crossAxisAlignment: CrossAxisAlignment.start,
                                    children: <Widget>[
                                        GestureDetector(
                                            child: Icon(Icons.chevron_left),
                                            onTap: () {
                                                setWeeksOrDays(0);
                                            },
                                        ),
                                        getTitle(),
                                        GestureDetector(
                                            child: Icon(Icons.chevron_right),
                                            onTap: () {
                                                setWeeksOrDays(2);
                                            },
                                        ),
                                    ],
                                ),
                            ),
                            Row(
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                children: <Widget>[
                                    Expanded(
                                        child: Center(
                                            child: Text('周日',
                                                style: TextStyle(
                                                    color: Sunday,
                                                ),
                                            ),
                                        ),
                                    ),
                                    Expanded(
                                        child: Center(
                                            child: Text('周一'),
                                        ),
                                    ),
                                    Expanded(
                                        child: Center(
                                            child: Text('周二'),
                                        ),
                                    ),
                                    Expanded(
                                        child: Center(
                                            child: Text('周三'),
                                        ),
                                    ),
                                    Expanded(
                                        child: Center(
                                            child: Text('周四'),
                                        ),
                                    ),
                                    Expanded(
                                        child: Center(
                                            child: Text('周五'),
                                        ),
                                    ),
                                    Expanded(
                                        child: Center(
                                            child: Text('周六',
                                                style: TextStyle(
                                                    color: Saturday,
                                                ),
                                            ),
                                        ),
                                    ),
                                ],
                            ),
                            Expanded(
                                child: Container(
                                    key: _containerKey,
                                    decoration: BoxDecoration(
                                        border: Border(
                                            // top: BorderSide(color: Colors.grey, width: 0.5),
                                            bottom: BorderSide(color: Colors.grey, width: 0.5),
                                        ),
                                    ),
                                    child: PageView.builder(
                                        itemCount: 3,
                                        physics: ScrollPhysics(),
                                        onPageChanged: (index) {
                                            // print('     onPageChanged : $index');
                                            setWeeksOrDays(index);
                                        },
                                        controller: _controller,
                                        itemBuilder: (context, index) {
                                            // print(' slideIndex : $index');
                                            return getBuilder(index);
                                        },
                                        pageSnapping: true,
                                    ),
                                ),
                            ),
                        ],
                    ),
                );
            },
        );
    }
    
    static DateTime format(DateTime dateTime) {
        return DateTime(dateTime.year, dateTime.month, dateTime.day);
    }
    
    void setDates(int page) {
        // print('  page      : $page');
        // print('--------------------------');
        if (page == -1) {
            List<DateTime> dates = [
                DateTime(this._selectedDate.year, this._selectedDate.month - 1, 1),
                DateTime(this._selectedDate.year, this._selectedDate.month, 1),
                DateTime(this._selectedDate.year, this._selectedDate.month + 1, 1),
            ];
            setWeekDay(dates);
            // print('_startWeekday      : $_startWeekday');
            // print('_endWeekday        : $_endWeekday');
            this._dates = dates;
            // print(' dates      : $dates');
            return;
        }
        
        List<DateTime> dates = this._dates;
        DateTime currentDate = dates[page];
        
        dates[0] = DateTime(currentDate.year, currentDate.month - 1, 1);
        dates[1] = DateTime(currentDate.year, currentDate.month, 1);
        dates[2] = DateTime(currentDate.year, currentDate.month + 1, 1);
        
        setState(() {
            setWeekDay(dates);
            this._selectedDate = dates[1];
            this._dates = dates;
        });
        
        // print(' dates      : $dates');
        // print('  page      : $page');
        _controller.animateToPage(1, duration: Duration(seconds: 100), curve: Threshold(0.0));

        if(_onCalendarChanged != null && page != 1){
            WidgetsBinding.instance.addPostFrameCallback((_) {
                _onCalendarChanged(_selectedDate);
            });
        }
    }
    
    void setWeeks(int page) {
        if (page == -1) {
            DateTime now = this._selectedDate;
            List<DateTime> week0 = _getDaysInWeek(now.subtract(new Duration(days: 7)));
            List<DateTime> week1 = _getDaysInWeek(now);
            List<DateTime> week2 = _getDaysInWeek(now.add(new Duration(days: 7)));
            
            this._weeks = [
                week0,
                week1,
                week2,
            ];
            return;
        }
        
        DateTime curr;
        List<List<DateTime>> newWeeks = this._weeks;
        if (page == 0) {
            curr = _weeks[0].first;
            newWeeks[0] = _getDaysInWeek(DateTime(curr.year, curr.month, curr.day - 7));
            newWeeks[1] = _getDaysInWeek(curr);
            newWeeks[2] = _getDaysInWeek(DateTime(curr.year, curr.month, curr.day + 7));
            page += 1;
        } else
        if (page == 2) {
            curr = _weeks[2].first;
            newWeeks[1] = _getDaysInWeek(curr);
            newWeeks[0] = _getDaysInWeek(DateTime(curr.year, curr.month, curr.day - 7));
            newWeeks[2] = _getDaysInWeek(DateTime(curr.year, curr.month, curr.day + 7));
            page -= 1;
        }
        setState(() {
            this._weeks = newWeeks;
        });
        
        _controller.animateToPage(page, duration: Duration(milliseconds: 1), curve: Threshold(0.0));
    }
    
    void setWeekDay(dates) {
        _startWeekday = (dates[1].weekday == 7 ? 0 : dates[1].weekday) - widget.firstDayOfWeek;
        _endWeekday = dates[2].weekday - widget.firstDayOfWeek;
    }
    
    AnimatedBuilder getBuilder(index) {
        switch (widget.viewType) {
            case ViewType.year:
                return yearBuilder(index);
            case ViewType.month:
                return monthBuilder(index);
            case ViewType.week:
                return weekBuilder(index);
            default:
                return null;
        }
    }
    
    Widget getTitle() {
        switch (widget.viewType) {
            case ViewType.year:
                return Text('Year View');
            case ViewType.month:
                return Text('${_selectedDate.year}-${_selectedDate.month.toString().padLeft(2, '0')}');
            case ViewType.week:
                return Text('${_weeks[1].first.year}-${_weeks[1].first.month.toString().padLeft(2, '0')}');
            default:
                return null;
        }
    }
    
    void setWeeksOrDays(index) {
        switch (widget.viewType) {
            case ViewType.year:
                break;
            case ViewType.month:
                setDates(index);
                break;
            case ViewType.week:
                setWeeks(index);
                break;
            default:
                break;
        }
    }
    
    changeSelectedDay(now) {
        setState(() {
            _selectedDate = now;
        });
        // print('_selectedDate : $_selectedDate');
    }
    
    List<Widget> daysEventList(now, textStyle, weekHeight, isToday, isSelectedDay, isThisMonthDay) {
        List events = _eventDatesMap.getEvents(now);
        double height = 16;
        int count = (weekHeight / 18).toInt() - 2; // toInt() == floor();
        int left = events.length - count;
        List<Widget> weekDays = [
            _dateWidget != null ? _dateWidget(now, isToday, isSelectedDay, isThisMonthDay) : Container(
                height: height,
                color: isToday ? Color(0xFFFEECC0) : (isSelectedDay ? Color(0xFFF4F5F6) : Colors.white),
                alignment: Alignment.center,
                child: Text('${now.day}', style: textStyle, textAlign: TextAlign.center,),
            )
        ];
        List usedEvents = events.take(count).toList();
        weekDays.addAll(usedEvents.map((event) {
            return _eventWidget != null ? _eventWidget(event, isToday, isSelectedDay, isThisMonthDay) : Container(
                height: height,
                padding: EdgeInsets.symmetric(horizontal: 0, vertical: 0),
                // margin: EdgeInsets.only(top: 2, bottom: 2, left: 3, right: 3),
                // decoration: BoxDecoration(
                //     borderRadius: BorderRadius.all(Radius.circular(3)),
                //     color: Color(0xFF2ECC87),
                // ),
                //child: Center(
                child: Text(
                    event.title,
                    style: event.style ?? TextStyle(fontSize: 10, color: Color(0xFF2ECC87)),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                ),
                //),
            );
        }));
        if (left > 0) {
            weekDays.add(
                Expanded(
                    child: Stack(
                        children: [
                            Positioned(
                                right: 0,
                                bottom: 0,
                                child: CustomPaint(
                                    painter: TrianglePainter(),
                                    child: Container(
                                        width: 26,
                                        height: 22,
                                    ),
                                ),
                            ),
                            Positioned(
                                right: 2,
                                bottom: 1,
                                child: Text(
                                    '+$left',
                                    textAlign: TextAlign.right,
                                    style: TextStyle(fontSize: 10, color: Colors.white),
                                ),
                            ),
                        ],
                    ),
                ),
            );
        }
        // print('          count : $count');
        // print('           left : $left');
        // print('  events.length : ${events.length}');
        // print('weekDays.length : ${weekDays.length}');
        // print('--------------');
        return weekDays;
    }
    
    AnimatedBuilder weekBuilder(int slideIndex) {
        List<DateTime> weekDays = _weeks[slideIndex];
        // print('weekDays : $weekDays');
        double pageWidth = _viewSize.width / 7;
        double pageHeight = (_viewSize.height - widget.headerHeight - WeekHeight);
        double weekHeight = pageHeight;
        // print(' viewSizeWidth : ${_viewSize.width}');
        // print('viewSizeHeight : ${_viewSize.height}');
        // print('  headerHeight : ${widget.headerHeight}');
        // print('   AspectRatio : ${pageWidth / pageHeight}');
        // print('    itemHeight : $weekHeight');
        
        // print();
        
        return AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
                return Center(
                    child: child,
                );
            },
            child: GridView.count(
                shrinkWrap: false,
                physics: NeverScrollableScrollPhysics(),
                crossAxisCount: 7,
                childAspectRatio: pageWidth / pageHeight,
                padding: EdgeInsets.zero,
                children: List.generate(7, (index) {
                    DateTime dateNow = format(DateTime.now());
                    DateTime weekDay = weekDays[index];
                    int year = weekDay.year;
                    int month = weekDay.month;
                    int day = weekDay.day;
                    bool isToday = weekDay.day == dateNow.day && weekDay.month == dateNow.month && weekDay.year == dateNow.year;
                    bool isPrevMonthDay = weekDay.month < this._selectedDate.month;
                    bool isNextMonthDay = weekDay.month > this._selectedDate.month;
                    bool isThisMonthDay = !isPrevMonthDay && !isNextMonthDay;
                    TextStyle textStyle = TextStyle(color: Colors.black);
                    
                    DateTime now = DateTime(year, month, day);
                    
                    if (isPrevMonthDay) {
                        // now = now.subtract(Duration(days: _startWeekday - index));
                        textStyle = TextStyle(
                            color: Colors.grey,
                        );
                    } else
                    if (isThisMonthDay) {
                        // now = DateTime(year, month, index + 1 - _startWeekday);
                    } else
                    if (isNextMonthDay) {
                        // now = DateTime(year, month, index + 1 - _startWeekday);
                        textStyle = TextStyle(color: Colors.grey);
                    }
                    if (isToday) {
                        textStyle = TextStyle(
                            color: Colors.redAccent,
                        );
                    }
                    
                    if (now.weekday == 6) {
                        textStyle = TextStyle(
                            color: Saturday,
                        );
                    }
                    if (now.weekday == 7) {
                        textStyle = TextStyle(
                            color: Sunday,
                        );
                    }
                    
                    bool isCurrentWeek = Utils.isSameWeek(now, DateTime.now());
                    
                    bool isSelectedDay = Utils.isSameDay(now, _selectedDate);
                    
                    return Container(
                        decoration: BoxDecoration(
                            border: Border(
                                top: isCurrentWeek ? BorderSide(color: Color(0xFFFDC02D), width: 3) : BorderSide(color: Colors.grey, width: 0.5),
                                // right: BorderSide(color: Colors.grey, width: 0.5),
                            ),
                            // color: isToday ? Color(0xFFF4F5F6) : (isSelectedDay ? Color(0xFFFFF5F6) : Colors.white),
                            color: isSelectedDay ? Color(0xFFF4F5F6) : Colors.white,
                        ),
                        child: Column(
                            children: daysEventList(now, textStyle, weekHeight, isToday, isSelectedDay, isThisMonthDay),
                        ),
                    );
                }),
            ),
        );
    }
    
    AnimatedBuilder monthBuilder(int slideIndex) {
        int totalDayCount = widget.isFixedDayCount ? 42 : (DateTime(_dates[slideIndex].year, _dates[slideIndex].month + 1, 0,).day + _startWeekday + (7 - _endWeekday));
        int year = _dates[slideIndex].year;
        int month = _dates[slideIndex].month;
        double pageWidth = _viewSize.width / 7;
        double pageHeight = (_viewSize.height - widget.headerHeight - WeekHeight) / (totalDayCount / 7);
        
        double weekHeight = pageWidth / (pageWidth / pageHeight);
        // print(' viewSizeWidth : ${_viewSize.width}');
        // print('viewSizeHeight : ${_viewSize.height}');
        // print('  headerHeight : ${widget.headerHeight}');
        // print('   AspectRatio : ${pageWidth / pageHeight}');
        // print('    itemHeight : $weekHeight');
        
        print('    screenWidth : ${MediaQuery.of(context).size.width}');   // 屏幕宽度 pt
        print('   screenHeight : ${MediaQuery.of(context).size.height}');  // 屏幕高度 pt
        // print('      pageWidth : $pageWidth');
        // print('     pageHeight : $pageHeight');
        print('     paddingTop : ${MediaQuery.of(context).padding.top}');    // statusBar 高度 pt  类似 iPhone X 的安全区域顶部
        print('  paddingBottom : ${MediaQuery.of(context).padding.bottom}'); // 底部栏 高度 pt  类似 iPhone X 的安全区域底部
        // print('         appBar : ${appBar.preferredSize.height}'); // 标题栏高度，如果设置过 AppBar 的 preferredSize高度的话，否则使用 kToolbarHeight
        // print(' kToolbarHeight : $kToolbarHeight'); // 默认标题栏的高度。
        // print('              data : $_dates');
        // print('     _selectedDate : $_selectedDate');
        // print('_dates[slideIndex] : ${_dates[slideIndex]}');
        // print('     _startWeekday : $_startWeekday');
        // print('       _endWeekday : $_endWeekday');
        // print('    totalDayCount : $totalDayCount');
        // print('------------------------------------');
        return AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
                return Center(
                    child: child,
                );
            },
            child: GridView.count(
                shrinkWrap: false,
                physics: NeverScrollableScrollPhysics(),
                crossAxisCount: 7,
                childAspectRatio: pageWidth / pageHeight,
                padding: EdgeInsets.zero,
                children: List.generate(totalDayCount, (index) {
                    bool isToday = DateTime
                        .now()
                        .day == (index + 1 - _startWeekday) && DateTime
                        .now()
                        .month == month && DateTime
                        .now()
                        .year == year;
                    bool isPrevMonthDay = index < _startWeekday;
                    bool isNextMonthDay = index >= (DateTime(year, month + 1, 0).day) + _startWeekday;
                    bool isThisMonthDay = !isPrevMonthDay && !isNextMonthDay;
                    TextStyle textStyle = TextStyle(color: Colors.black);
                    DateTime now = DateTime(year, month, 1);
                    if (isPrevMonthDay) {
                        now = now.subtract(Duration(days: _startWeekday - index));
                        textStyle = TextStyle(
                            color: Colors.grey,
                        );
                    } else
                    if (isThisMonthDay) {
                        now = DateTime(year, month, index + 1 - _startWeekday);
                    } else
                    if (isNextMonthDay) {
                        now = DateTime(year, month, index + 1 - _startWeekday);
                        textStyle = TextStyle(color: Colors.grey);
                    }
                    if (isToday) {
                        textStyle = TextStyle(
                            color: Colors.redAccent,
                        );
                    }
                    
                    if (now.weekday == 6) {
                        textStyle = TextStyle(
                            color: Saturday,
                        );
                    }
                    if (now.weekday == 7) {
                        textStyle = TextStyle(
                            color: Sunday,
                        );
                    }
                    
                    bool isCurrentWeek = Utils.isSameWeek(now, DateTime.now());
                    
                    bool isSelectedDay = Utils.isSameDay(now, _selectedDate);
                    
                    return GestureDetector(
                        onTap: () {
                            changeSelectedDay(now);
                            if (_onDayPressed != null) {
                                _onDayPressed(now, isToday);
                            }
                        },
                        //onDoubleTap: (){
                        //    changeSelectedDay(now);
                        //},
                        child: Container(
                            decoration: BoxDecoration(
                                border: Border(
                                    top: isCurrentWeek ? BorderSide(color: Color(0xFFFDC02D), width: 3) : BorderSide(color: Colors.grey, width: 0.5),
                                    // right: BorderSide(color: Colors.grey, width: 0.5),
                                ),
                                // color: isToday ? Color(0xFFF4F5F6) : (isSelectedDay ? Color(0xFFFFF5F6) : Colors.white),
                                color: isSelectedDay ? Color(0xFFF4F5F6) : Colors.white,
                            ),
                            child: Column(
                                children: daysEventList(now, textStyle, weekHeight, isToday, isSelectedDay, isThisMonthDay),
                            ),
                        ),
                    );
                }),
            ),
        );
    }
    
    AnimatedBuilder yearBuilder(int slideIndex) {
        return AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
                return Center(
                    child: child,
                );
            },
            child: GridView.count(
                shrinkWrap: false,
                physics: NeverScrollableScrollPhysics(),
                crossAxisCount: 7,
                childAspectRatio: 1,
                padding: EdgeInsets.zero,
                children: List.generate(5, (index) {
                    return Text(index.toString());
                })
            ),
        );
    }
    
    List<DateTime> _getDaysInWeek([DateTime selectedDate]) {
        if (selectedDate == null) {
            selectedDate = DateTime.now();
        }
        
        var firstDayOfCurrentWeek = Utils.firstDayOfWeek(selectedDate);
        var lastDayOfCurrentWeek = Utils.lastDayOfWeek(selectedDate);
        
        return Utils.daysInRange(firstDayOfCurrentWeek, lastDayOfCurrentWeek).toList();
    }
}
